﻿using System;
using System.Linq;
using SFML.Graphics;
using Transformable_Engine_v2.Engine.Animation.ColorAnimation;
using Transformable_Engine_v2.Engine.Animation.FrameAnimation;
using Transformable_Engine_v2.Engine.Animation.RotationAnimation;
using Transformable_Engine_v2.Engine.Animation.ScaleAnimation;
using Transformable_Engine_v2.Engine.Global;
using Transformable_Engine_v2.Engine.Helper;
using Transformable_Engine_v2.Engine.Rendering;
using Transformable_Engine_v2.Engine.Transformable_Math.Geometry;
using Color = Transformable_Engine_v2.Engine.Graphics.Color;
using Sprite = Transformable_Engine_v2.Engine.Graphics.Sprite;

namespace Transformable_Engine_v2.Engine.GameObjects.Containers
{
    public class ImageContainer : Container, IColorAnimatable, IScaleAnimatable, IFrameAnimatable, IRotationAnimatable
    {
        protected readonly SFML.Graphics.Sprite _drawableObject;

        public override TE_Vector2 AbsolutePosition
        {
            protected set
            {
                base.AbsolutePosition = value;
                if(_drawableObject != null)
                    _drawableObject.Position = AbsolutePosition.ToVector2f();
            }
        }

        protected TE_Rectangle _source;
        public Sprite Sprite
        {
            set 
            { 
                _drawableObject.Texture = value.Texture;
                _source = value.SpriteArea;
                _drawableObject.TextureRect = _source.ToIntRect();

                if (_mirrorState != MirrorState.None)
                    Mirror = _mirrorState;
            }
        }

        private Color _color;
        public Color Color
        {
            get { return _color; }
            set 
            { 
                _color = value;
                _drawableObject.Color = _color.ToSFMLColor();
            }
        }

        private TE_Vector2 _scale;
        public TE_Vector2 Scale
        {
            get { return _scale; }
            set
            {
                _scale = value;
                _drawableObject.Scale = _scale.ToVector2f();
            }
        }

        public float ScaleX
        {
            set
            {
                Scale = new TE_Vector2(value, Scale.Y);
            }
        }

        public float ScaleY
        {
            set
            {
                Scale = new TE_Vector2(Scale.X, value);
            }
        }

        private double _rotation;
        public double Rotation
        {
            get { return _rotation; }
            set 
            { 
                _rotation = value.TimesTwoPi();
                _drawableObject.Rotation = (float) _rotation.RadiansToDegrees();
            }
        }

        public double RotationAbsolute
        {
            get { return _rotation; }
            set
            {
                _rotation = value;
                _drawableObject.Rotation = (float)_rotation.RadiansToDegrees();
            }
        }

        private TE_Vector2 _origin;
        public TE_Vector2 OriginAbsolute
        {
            set 
            { 
                _origin = value;
                _drawableObject.Origin = _origin.ToVector2f();
            }
        }

        public TE_Rectangle.Alignment Origin
        {
            set
            {
                if (_source == null)
                    throw new Exception("There is no source defined");

                switch (value)
                {
                    case TE_Rectangle.Alignment.TopLeft:
                        OriginAbsolute = TE_Vector2.Zero;
                        break;
                    case TE_Rectangle.Alignment.TopMiddle:
                        OriginAbsolute = _source.TopMiddle - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.TopRight:
                        OriginAbsolute = _source.TopRight - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.CenterLeft:
                        OriginAbsolute = _source.CenterLeft - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.CenterMiddle:
                        OriginAbsolute = _source.CenterMiddle - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.CenterRight:
                        OriginAbsolute = _source.CenterRight - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.BottomLeft:
                        OriginAbsolute = _source.BottomLeft - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.BottomMiddle:
                        OriginAbsolute = _source.BottomMiddle - _source.TopLeft;
                        break;
                    case TE_Rectangle.Alignment.BottomRight:
                        OriginAbsolute = _source.BottomRight - _source.TopLeft;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("value");
                }

                
            }
        }

        public enum MirrorState
        {
            None,
            MirrorHorizontal,
            MirrorVertical,
            MirrorHorizontalAndVertical,
        }

        private MirrorState _mirrorState;
        public MirrorState Mirror
        {
            set 
            { 
                if(_source == null)
                        throw new Exception("Cannot assign mirror before source");

                IntRect newSource;
                switch(value)
                {
                    case MirrorState.None:
                        newSource = _source.ToIntRect();
                        break;
                    case MirrorState.MirrorHorizontal:
                        newSource = new TE_Rectangle(_source.Right, _source.Y, -_source.Width, _source.Height).ToIntRect();
                        break;
                    case MirrorState.MirrorVertical:
                        newSource = new TE_Rectangle(_source.X, _source.Bottom, _source.Width, -_source.Height).ToIntRect();
                        break;
                    case MirrorState.MirrorHorizontalAndVertical:
                        newSource = new TE_Rectangle(_source.Right, _source.Bottom, -_source.Width, -_source.Height).ToIntRect();
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("value");
                }
                _mirrorState = value;
                _drawableObject.TextureRect = newSource;
            }
        }

        public ImageContainer()
        {
            _drawableObject = new SFML.Graphics.Sprite();
            Color = Color.White;
            Scale = TE_Vector2.One;
            Rotation = 0;
        }

        public override void Draw()
        {
            if(Visible)
            {
                if(ShaderHandler.HasShader)
                    RenderTargetHandler.GetCurrentRenderTarget().Draw(_drawableObject, ShaderHandler.CurrentShader);
                else
                    RenderTargetHandler.GetCurrentRenderTarget().Draw(_drawableObject);
            
                base.Draw();
            }
        }

        /// <summary>
        /// Sets the affected object to an animation
        /// </summary>
        /// <param name="animation">Animation to set the affected object</param>
        /// <returns></returns>
        protected override bool SetAnimationAffectedObject(Animation.BaseAnimation animation)
        {
            if(animation is FrameAnimation) 
            {
                (animation as FrameAnimation).AffectedObject = this;
                return true;
            }
            if (animation is ColorAnimation) 
            {
                (animation as ColorAnimation).AffectedObject = this;
                return true;
            }
            if (animation is RotationAnimation)
            {
                (animation as RotationAnimation).AffectedObject = this;
                return true;
            }
            if (animation is RotationUniformLinearAnimation)
            {
                (animation as RotationUniformLinearAnimation).AffectedObject = this;
                return true;
            } 
            if (animation is ScaleUniformLinearAnimation)
            {
                (animation as ScaleUniformLinearAnimation).AffectedObject = this;
                return true;
            } 
            if (animation is ScaleAnimation) 
            {
                (animation as ScaleAnimation).AffectedObject = this;
                return true;
            }
            return base.SetAnimationAffectedObject(animation);
        }

        public Color GetColor()
        {
            return Color;
        }

        public void AddToColor(Color color)
        {
            Color += color;
            Color.Clamp();
        }

        /// <summary>
        /// Gets the scale of the image
        /// </summary>
        /// <returns></returns>
        public TE_Vector2 GetScale()
        {
            return Scale;
        }

        /// <summary>
        /// Adds to the scale of the image
        /// </summary>
        /// <param name="scale"></param>
        public void AddToScale(TE_Vector2 scale)
        {
            Scale += scale;
        }

        /// <summary>
        /// Sets the current frame, for Frame by Frame animations
        /// </summary>
        /// <param name="sprite"></param>
        public void SetFrame(Sprite sprite)
        {
            Sprite = sprite;
        }

        public void ClearOtherFrameAnimations(FrameAnimation frameAnimation)
        {
            var animationsToClear = Animations.OfType<FrameAnimation>().Where(x => x != frameAnimation).ToList();

            for (int i = 0; i < animationsToClear.Count(); i++)
            {
                animationsToClear[i].Die();
                Animations.Remove(animationsToClear[i]);
            }
        }

        public double GetRotation()
        {
            return Rotation;
        }

        public void AddToRotation(double rotation)
        {
            RotationAbsolute += rotation.TimesTwoPi();
        }
    }
}